/**
 * 验证UT生成规则是否符合真实项目实践
 * 
 * 分析OHCode下所有test.cpp文件，统计：
 * 1. TEST_F vs HWTEST_F vs TEST使用率
 * 2. SetUp/TearDown使用率
 * 3. Mock类定义位置和方式
 * 4. SetArgReferee/SetArgPointee使用情况
 * 5. 智能指针 vs 栈对象使用
 * 6. EXPECT_CALL使用模式
 * 7. 头文件包含模式
 * 8. Mock类定义是否在文件开头
 */

const fs = require('fs');
const path = require('path');

const stats = {
    totalTestFiles: 0,
    
    // 测试框架使用
    testFramework: {
        TEST_F: 0,
        HWTEST_F: 0,
        TEST: 0,
        GTEST_TEST: 0
    },
    
    // SetUp/TearDown
    setupTeardown: {
        hasSetUp: 0,
        hasTearDown: 0,
        hasBoth: 0
    },
    
    // 出参Mock
    outParamMock: {
        hasSetArgReferee: 0,
        hasSetArgPointee: 0,
        hasDoAll: 0,
        hasExpectCallWithWillOnce: 0
    },
    
    // 智能指针使用
    pointerUsage: {
        make_shared: 0,
        shared_ptr: 0,
        unique_ptr: 0,
        stackObject: 0,  // 估算
        sptr: 0
    },
    
    // Mock类定义
    mockClass: {
        hasMockClassDef: 0,
        mockClassInFileHeader: 0,  // Mock类在文件前30%
        mockClassLocation: []  // 记录位置百分比
    },
    
    // 头文件包含
    headers: {
        hasGtest: 0,
        hasGmock: 0,
        hasSetArgRefereeUsing: 0,
        hasDoAllUsing: 0,
        mockHeaderInclude: 0  // #include "mock_xxx.h"
    },
    
    // 测试成员变量
    testMembers: {
        hasInstanceMember: 0,  // instance_ 模式
        hasMockMember: 0,      // mockXxx_ 模式
        hasSharedPtrMember: 0
    },
    
    // EXPECT_CALL模式
    expectCallPatterns: {
        withMatchersOnly: 0,        // EXPECT_CALL(*mock, Func(_))
        withDoAll: 0,                // DoAll(SetArgReferee, Return)
        withTimes: 0,                // .Times(N)
        withWillOnce: 0,             // .WillOnce
        withWillRepeatedly: 0        // .WillRepeatedly
    },
    
    // 文件组织
    fileOrganization: {
        totalLines: 0,
        avgLinesPerFile: 0,
        filesWithComments: 0,
        filesWithNamespace: 0
    }
};

// 示例收集
const examples = {
    mockClassDef: [],
    setArgRefereeUsage: [],
    setupTeardown: [],
    testFramework: []
};

/**
 * 递归查找test.cpp文件
 */
function findTestFiles(dir, maxDepth = 10, currentDepth = 0) {
    const testFiles = [];
    
    if (currentDepth > maxDepth) return testFiles;
    
    try {
        const items = fs.readdirSync(dir, { withFileTypes: true });
        
        for (const item of items) {
            if (!item.isDirectory()) {
                if (item.name.endsWith('test.cpp') || item.name.endsWith('_test.cpp')) {
                    testFiles.push(path.join(dir, item.name));
                }
                continue;
            }
            
            const fullPath = path.join(dir, item.name);
            
            // 跳过特定目录
            if (item.name.includes('node_modules') || 
                item.name.includes('.git') ||
                item.name.includes('out') ||
                item.name.includes('build') ||
                item.name.includes('fuzztest')) {
                continue;
            }
            
            const subFiles = findTestFiles(fullPath, maxDepth, currentDepth + 1);
            testFiles.push(...subFiles);
        }
    } catch (err) {
        // Ignore
    }
    
    return testFiles;
}

/**
 * 分析单个test文件
 */
function analyzeTestFile(filePath) {
    try {
        const content = fs.readFileSync(filePath, 'utf-8');
        const lines = content.split('\n');
        stats.fileOrganization.totalLines += lines.length;
        
        // 1. 测试框架
        const testFCount = (content.match(/TEST_F\s*\(/g) || []).length;
        const hwtestFCount = (content.match(/HWTEST_F\s*\(/g) || []).length;
        const testCount = (content.match(/\bTEST\s*\(/g) || []).length - testFCount - hwtestFCount;
        
        if (testFCount > 0) stats.testFramework.TEST_F++;
        if (hwtestFCount > 0) stats.testFramework.HWTEST_F++;
        if (testCount > 0) stats.testFramework.TEST++;
        
        // 2. SetUp/TearDown
        const hasSetUp = /void\s+SetUp\s*\(\s*\)\s*override/.test(content);
        const hasTearDown = /void\s+TearDown\s*\(\s*\)\s*override/.test(content);
        
        if (hasSetUp) stats.setupTeardown.hasSetUp++;
        if (hasTearDown) stats.setupTeardown.hasTearDown++;
        if (hasSetUp && hasTearDown) stats.setupTeardown.hasBoth++;
        
        // 3. 出参Mock
        if (/SetArgReferee/.test(content)) {
            stats.outParamMock.hasSetArgReferee++;
            if (examples.setArgRefereeUsage.length < 5) {
                const match = content.match(/SetArgReferee<\d+>\([^)]+\)/);
                if (match) {
                    examples.setArgRefereeUsage.push({
                        file: filePath,
                        usage: match[0]
                    });
                }
            }
        }
        if (/SetArgPointee/.test(content)) stats.outParamMock.hasSetArgPointee++;
        if (/DoAll\s*\(/.test(content)) stats.outParamMock.hasDoAll++;
        if (/EXPECT_CALL.*WillOnce/.test(content)) stats.outParamMock.hasExpectCallWithWillOnce++;
        
        // 4. 智能指针
        if (/std::make_shared/.test(content)) stats.pointerUsage.make_shared++;
        if (/std::shared_ptr/.test(content)) stats.pointerUsage.shared_ptr++;
        if (/std::unique_ptr/.test(content)) stats.pointerUsage.unique_ptr++;
        if (/\bsptr</.test(content)) stats.pointerUsage.sptr++;
        
        // 5. Mock类定义
        const mockClassMatch = content.match(/class\s+Mock\w+\s*:\s*public/);
        if (mockClassMatch) {
            stats.mockClass.hasMockClassDef++;
            
            // 计算Mock类位置
            const mockClassLine = content.substring(0, content.indexOf(mockClassMatch[0])).split('\n').length;
            const locationPercent = (mockClassLine / lines.length) * 100;
            stats.mockClass.mockClassLocation.push(locationPercent);
            
            if (locationPercent < 30) {
                stats.mockClass.mockClassInFileHeader++;
            }
            
            if (examples.mockClassDef.length < 5) {
                const startIdx = content.indexOf(mockClassMatch[0]);
                const snippet = content.substring(startIdx, startIdx + 300);
                examples.mockClassDef.push({
                    file: filePath,
                    location: `${mockClassLine}/${lines.length} (${locationPercent.toFixed(1)}%)`,
                    snippet: snippet.substring(0, 200)
                });
            }
        }
        
        // 6. 头文件
        if (/#include\s+<gtest\/gtest\.h>/.test(content)) stats.headers.hasGtest++;
        if (/#include\s+<gmock\/gmock\.h>/.test(content)) stats.headers.hasGmock++;
        if (/using\s+testing::SetArgReferee/.test(content)) stats.headers.hasSetArgRefereeUsing++;
        if (/using\s+testing::DoAll/.test(content)) stats.headers.hasDoAllUsing++;
        if (/#include\s+"mock_\w+\.h"/.test(content)) stats.headers.mockHeaderInclude++;
        
        // 7. 测试成员变量
        if (/\w+_\s*;\s*\/\/.*instance|instance_/.test(content)) stats.testMembers.hasInstanceMember++;
        if (/mock\w+_/.test(content)) stats.testMembers.hasMockMember++;
        if (/std::shared_ptr<\w+>\s+\w+_/.test(content)) stats.testMembers.hasSharedPtrMember++;
        
        // 8. EXPECT_CALL模式
        const expectCalls = content.match(/EXPECT_CALL\s*\([^)]+\)/g) || [];
        if (expectCalls.length > 0) {
            if (/DoAll/.test(content)) stats.expectCallPatterns.withDoAll++;
            if (/\.Times\(/.test(content)) stats.expectCallPatterns.withTimes++;
            if (/\.WillOnce/.test(content)) stats.expectCallPatterns.withWillOnce++;
            if (/\.WillRepeatedly/.test(content)) stats.expectCallPatterns.withWillRepeatedly++;
        }
        
        // 9. 文件组织
        if (/\/\*.*Copyright/.test(content)) stats.fileOrganization.filesWithComments++;
        if (/namespace\s+\w+/.test(content)) stats.fileOrganization.filesWithNamespace++;
        
    } catch (err) {
        console.error(`Error analyzing ${filePath}: ${err.message}`);
    }
}

/**
 * 生成报告
 */
function generateReport() {
    stats.fileOrganization.avgLinesPerFile = 
        Math.round(stats.fileOrganization.totalLines / stats.totalTestFiles);
    
    // 计算Mock类位置平均值
    if (stats.mockClass.mockClassLocation.length > 0) {
        const avgLocation = stats.mockClass.mockClassLocation.reduce((a, b) => a + b, 0) / 
                           stats.mockClass.mockClassLocation.length;
        stats.mockClass.avgLocation = avgLocation.toFixed(1) + '%';
    }
    
    const report = {
        summary: {
            totalTestFiles: stats.totalTestFiles,
            avgLinesPerFile: stats.fileOrganization.avgLinesPerFile
        },
        
        testFramework: {
            ...stats.testFramework,
            percentages: {
                TEST_F: ((stats.testFramework.TEST_F / stats.totalTestFiles) * 100).toFixed(2) + '%',
                HWTEST_F: ((stats.testFramework.HWTEST_F / stats.totalTestFiles) * 100).toFixed(2) + '%',
                TEST: ((stats.testFramework.TEST / stats.totalTestFiles) * 100).toFixed(2) + '%'
            }
        },
        
        setupTeardown: {
            ...stats.setupTeardown,
            percentages: {
                hasSetUp: ((stats.setupTeardown.hasSetUp / stats.totalTestFiles) * 100).toFixed(2) + '%',
                hasTearDown: ((stats.setupTeardown.hasTearDown / stats.totalTestFiles) * 100).toFixed(2) + '%',
                hasBoth: ((stats.setupTeardown.hasBoth / stats.totalTestFiles) * 100).toFixed(2) + '%'
            }
        },
        
        outParamMock: {
            ...stats.outParamMock,
            percentages: {
                hasSetArgReferee: ((stats.outParamMock.hasSetArgReferee / stats.totalTestFiles) * 100).toFixed(2) + '%',
                hasDoAll: ((stats.outParamMock.hasDoAll / stats.totalTestFiles) * 100).toFixed(2) + '%'
            }
        },
        
        pointerUsage: {
            ...stats.pointerUsage,
            percentages: {
                make_shared: ((stats.pointerUsage.make_shared / stats.totalTestFiles) * 100).toFixed(2) + '%',
                shared_ptr: ((stats.pointerUsage.shared_ptr / stats.totalTestFiles) * 100).toFixed(2) + '%'
            }
        },
        
        mockClass: {
            ...stats.mockClass,
            percentages: {
                hasMockClassDef: ((stats.mockClass.hasMockClassDef / stats.totalTestFiles) * 100).toFixed(2) + '%',
                mockClassInFileHeader: stats.mockClass.hasMockClassDef > 0 ? 
                    ((stats.mockClass.mockClassInFileHeader / stats.mockClass.hasMockClassDef) * 100).toFixed(2) + '%' : '0%'
            }
        },
        
        headers: {
            ...stats.headers,
            percentages: {
                hasGtest: ((stats.headers.hasGtest / stats.totalTestFiles) * 100).toFixed(2) + '%',
                hasGmock: ((stats.headers.hasGmock / stats.totalTestFiles) * 100).toFixed(2) + '%',
                hasSetArgRefereeUsing: stats.outParamMock.hasSetArgReferee > 0 ?
                    ((stats.headers.hasSetArgRefereeUsing / stats.outParamMock.hasSetArgReferee) * 100).toFixed(2) + '%' : '0%'
            }
        },
        
        testMembers: {
            ...stats.testMembers,
            percentages: {
                hasInstanceMember: ((stats.testMembers.hasInstanceMember / stats.totalTestFiles) * 100).toFixed(2) + '%',
                hasMockMember: ((stats.testMembers.hasMockMember / stats.totalTestFiles) * 100).toFixed(2) + '%'
            }
        },
        
        expectCallPatterns: stats.expectCallPatterns,
        
        examples: {
            mockClassDef: examples.mockClassDef.slice(0, 3),
            setArgRefereeUsage: examples.setArgRefereeUsage.slice(0, 3)
        }
    };
    
    return report;
}

/**
 * 主函数
 */
function main() {
    console.log('🔍 开始验证UT生成规则...\n');
    
    const ohCodePath = path.join(__dirname, '../OHCode');
    
    if (!fs.existsSync(ohCodePath)) {
        console.error('❌ OHCode目录不存在！');
        process.exit(1);
    }
    
    console.log('📂 查找所有test.cpp文件...');
    const testFiles = findTestFiles(ohCodePath);
    stats.totalTestFiles = testFiles.length;
    
    console.log(`✅ 找到 ${testFiles.length} 个test.cpp文件\n`);
    console.log('📊 开始分析...\n');
    
    let processed = 0;
    const progressInterval = Math.max(1, Math.floor(testFiles.length / 20));
    
    for (const file of testFiles) {
        analyzeTestFile(file);
        processed++;
        
        if (processed % progressInterval === 0 || processed === testFiles.length) {
            const percent = ((processed / testFiles.length) * 100).toFixed(1);
            console.log(`  进度: ${processed}/${testFiles.length} (${percent}%)`);
        }
    }
    
    console.log('\n✅ 分析完成！\n');
    console.log('📝 生成报告...\n');
    
    const report = generateReport();
    
    // 保存报告
    const reportPath = path.join(__dirname, '../UT_RULES_VALIDATION.json');
    fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
    
    console.log(`✅ 详细报告已保存: ${reportPath}\n`);
    
    // 打印摘要
    console.log('━'.repeat(80));
    console.log('                   📊 规则验证摘要');
    console.log('━'.repeat(80));
    console.log();
    
    console.log(`总test.cpp文件数: ${report.summary.totalTestFiles}`);
    console.log(`平均文件行数: ${report.summary.avgLinesPerFile}\n`);
    
    console.log('1️⃣ 测试框架使用:');
    console.log(`  - TEST_F: ${report.testFramework.percentages.TEST_F}`);
    console.log(`  - HWTEST_F: ${report.testFramework.percentages.HWTEST_F}`);
    console.log(`  - TEST: ${report.testFramework.percentages.TEST}\n`);
    
    console.log('2️⃣ SetUp/TearDown:');
    console.log(`  - 有SetUp: ${report.setupTeardown.percentages.hasSetUp}`);
    console.log(`  - 有TearDown: ${report.setupTeardown.percentages.hasTearDown}`);
    console.log(`  - 两者都有: ${report.setupTeardown.percentages.hasBoth}\n`);
    
    console.log('3️⃣ 出参Mock使用:');
    console.log(`  - 使用SetArgReferee: ${report.outParamMock.percentages.hasSetArgReferee}`);
    console.log(`  - 使用DoAll: ${report.outParamMock.percentages.hasDoAll}`);
    console.log(`  - 使用EXPECT_CALL+WillOnce: ${report.outParamMock.hasExpectCallWithWillOnce}个文件\n`);
    
    console.log('4️⃣ 智能指针使用:');
    console.log(`  - make_shared: ${report.pointerUsage.percentages.make_shared}`);
    console.log(`  - shared_ptr: ${report.pointerUsage.percentages.shared_ptr}`);
    console.log(`  - sptr: ${report.pointerUsage.sptr}个文件\n`);
    
    console.log('5️⃣ Mock类定义:');
    console.log(`  - 有Mock类定义: ${report.mockClass.percentages.hasMockClassDef}`);
    console.log(`  - Mock类在文件前30%: ${report.mockClass.percentages.mockClassInFileHeader}`);
    console.log(`  - Mock类平均位置: ${report.mockClass.avgLocation || 'N/A'}\n`);
    
    console.log('6️⃣ 头文件包含:');
    console.log(`  - 包含gtest.h: ${report.headers.percentages.hasGtest}`);
    console.log(`  - 包含gmock.h: ${report.headers.percentages.hasGmock}`);
    console.log(`  - 使用SetArgReferee的文件中有using声明: ${report.headers.percentages.hasSetArgRefereeUsing}`);
    console.log(`  - 包含mock_xxx.h: ${report.headers.mockHeaderInclude}个文件\n`);
    
    console.log('7️⃣ 测试成员变量:');
    console.log(`  - 有instance_成员: ${report.testMembers.percentages.hasInstanceMember}`);
    console.log(`  - 有mock成员: ${report.testMembers.percentages.hasMockMember}\n`);
    
    console.log('━'.repeat(80));
    console.log();
}

// 运行
main();

